﻿using NLog;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;
using XmlServer.Hosting.Service.Helpers;

namespace XmlServer.Hosting.Service
{
    
    public partial class XmlServerService : ServiceBase
    {
        private static Logger logger = LogManager.GetCurrentClassLogger();

        private Settings settings;
        private XmlServer.Server server;
        
        public XmlServerService()
        {
            InitializeComponent();
        }

        /// <summary>
        /// This methods is used by the unit test for testing the service
        /// </summary>
        public void Start()
        {
            OnStart(null);
        }
        public void Stop()
        {
            OnStop();
        }
        public void Pause()
        {
            OnPause();
        }
        public void Continue()
        {
            OnContinue();
        }

        
        protected override void OnStart(string[] args)
        {
            
            logger.Info(String.Format("{0} is Starting", Helpers.Common.ServiceName));

            // get the settings            
            try
            {
                logger.Info("+ Reading the XmlServer settings from config file");
                settings = new Settings();
                settings.ValidateSettings();
                settings.LogSettings(logger);
            }
            catch (Exception err)
            {
                logger.Error(err, "Failed to create the settings object in OnStart()");
                Common.LogEvent(Common.ServiceName, "Application", String.Format("Failed to create the settings object in OnStart(): {0}", err.ToString()), EventLogEntryType.Error);
                return;
            }

            // create the servers
            try
            {                
                CreateServer();
            }
            catch (Exception err)
            {
                logger.Error(err, "Failed to create the server object in OnStart()");
                Common.LogEvent(Common.ServiceName, "Application", String.Format("Failed to create the server object in OnStart(): {0}", err.ToString()), EventLogEntryType.Error);
                return;
            }

            // start the listener
            try
            {
                logger.Info("+ Start the XmlServer Listener");
                server.StartListening();
            }
            catch (Exception err)
            {
                logger.Error(err, "Failed to start the listener in OnStart()");
                Common.LogEvent(Common.ServiceName, "Application", String.Format("Failed to create the server object in OnStart(): {0}", err.ToString()), EventLogEntryType.Error);
                return;
            }


            try
            {
                base.OnStart(args);
            }
            catch (Exception err)
            {
                logger.Error(err, "Failed to call base.OnStart()");
                Common.LogEvent(Common.ServiceName, "Application", String.Format("Failed to call base.OnStart(): {0}", err.ToString()), EventLogEntryType.Error);                
            }


            // all done...log the message the we are started
            logger.Info("XmlServer is Started");

        }

        protected override void OnStop()
        {
            logger.Info(String.Format("{0} is Stopping", Helpers.Common.ServiceName));

            try
            {                
                server.Shutdown();
            }
            catch (Exception err)
            {
                logger.Error(err, "Failed to stop the service");
                Common.LogEvent(Common.ServiceName, "Application", String.Format("Failed to stop the service: {0}", err.ToString()), EventLogEntryType.Error);                
            }

            try
            {
                base.OnShutdown();
            }
            catch (Exception err)
            {
                logger.Error(err, "Failed to call base.OnShutDown()");
                Common.LogEvent(Common.ServiceName, "Application", String.Format("Failed to call base.OnShutDown(): {0}", err.ToString()), EventLogEntryType.Error);
            }


            logger.Info(String.Format("{0} is Stopped", Helpers.Common.ServiceName));

            
        }

        protected override void OnPause()
        {
            logger.Info(String.Format("{0} is Pausing", Helpers.Common.ServiceName));

            try
            {
                server.PauseListening();
            }
            catch (Exception err)
            {
                logger.Error(err, "Failed to pause the service");
                Common.LogEvent(Common.ServiceName, "Application", String.Format("Failed to pause the service: {0}", err.ToString()), EventLogEntryType.Error);
            }

            try
            {
                base.OnPause();
            }
            catch (Exception err)
            {
                logger.Error(err, "Failed to call base.OnPause()");
                Common.LogEvent(Common.ServiceName, "Application", String.Format("Failed to call base.OnPause(): {0}", err.ToString()), EventLogEntryType.Error);
            }


            logger.Info(String.Format("{0} is Paused", Helpers.Common.ServiceName));            
        }

        protected override void OnContinue()
        {
            logger.Info(String.Format("{0} is Continuing", Helpers.Common.ServiceName));

            try
            {
                server.ResumeListening();
            }
            catch (Exception err)
            {
                logger.Error(err, "Failed to Continue the service");
                Common.LogEvent(Common.ServiceName, "Application", String.Format("Failed to Continue the service: {0}", err.ToString()), EventLogEntryType.Error);
            }

            try
            {
                base.OnContinue();
            }
            catch (Exception err)
            {
                logger.Error(err, "Failed to call base.OnContinue()");
                Common.LogEvent(Common.ServiceName, "Application", String.Format("Failed to call base.OnContinue(): {0}", err.ToString()), EventLogEntryType.Error);
            }


            logger.Info(String.Format("{0} is Resumed", Helpers.Common.ServiceName));                     
        }


        private void CreateServer()
        {

            logger.Info("+ Creating the XML Server");

            server = new Server();

            server.ProcessingListener.Configuration.IPAddress = settings.ProcessorIpAddress;
            server.ProcessingListener.Configuration.Port = settings.ProcessingPort;
            server.ProcessingListener.Configuration.OperatorsFolder = settings.ProcessingOperatorsFolder;
            server.ProcessingListener.Configuration.NumberOfThreads = settings.NumberOfProcessorThreads;

            server.PriorityListener.Configuration.IPAddress = settings.PriorityIpAddress;
            server.PriorityListener.Configuration.Port = settings.PriorityPort;
            server.PriorityListener.Configuration.OperatorsFolder = settings.PriorityOperatorsFolder;
            server.PriorityListener.Configuration.NumberOfThreads = settings.NumberOfPriorityThreads;


            logger.Info("+ Setting up server events");

            // create the events for the listeners
            if (settings.LogDebugMessage)
            {
                server.ProcessingListener.ListenerDebugMessage += Listener_ListenerDebugMessage;
                server.PriorityListener.ListenerDebugMessage += Listener_ListenerDebugMessage;
            }

            if (settings.LogException)
            {
                server.ProcessingListener.ListenerException += Listener_ListenerException;
                server.PriorityListener.ListenerException += Listener_ListenerException;
            }

            if (settings.LogPaused)
            {
                server.ProcessingListener.ListenerPaused += Listener_ListenerPaused;
                server.PriorityListener.ListenerPaused += Listener_ListenerPaused;
            }

            if (settings.LogRequestCompleted)
            {
                server.ProcessingListener.ListenerRequestCompleted += Listener_ListenerRequestCompleted;
                server.PriorityListener.ListenerRequestCompleted += Listener_ListenerRequestCompleted;
            }

            if (settings.LogRequestServicing)
            {
                server.ProcessingListener.ListenerRequestServicing += Listener_ListenerRequestServicing;
                server.PriorityListener.ListenerRequestServicing += Listener_ListenerRequestServicing;
            }

            if (settings.LogRequestStarted)
            {
                server.ProcessingListener.ListenerRequestStarted += Listener_ListenerRequestStarted;
                server.PriorityListener.ListenerRequestStarted += Listener_ListenerRequestStarted;
            }

            if (settings.LogResumed)
            {
                server.ProcessingListener.ListenerResumed += Listener_ListenerResumed;
                server.PriorityListener.ListenerResumed += Listener_ListenerResumed;
            }

            if (settings.LogStarted)
            {
                server.ProcessingListener.ListenerStarted += Listener_ListenerStarted;
                server.PriorityListener.ListenerStarted += Listener_ListenerStarted;
            }

            if (settings.LogStopped)
            {
                server.ProcessingListener.ListenerStopped += Listener_ListenerStopped;
                server.PriorityListener.ListenerStopped += Listener_ListenerStopped;
            }
                                    
        }


        #region Listener Events

        public void Listener_ListenerDebugMessage(object sender, string IPAddress, int Port, string Message)
        {
            var listener = (XmlServer.Listeners.Listener)sender;
            logger.Info(String.Format("{0} Listener: {1}", listener.ListenerType, Message));
        }

        public void Listener_ListenerException(object sender, string ClassName, string MethodName, Exception err)
        {
            var listener = (XmlServer.Listeners.Listener)sender;
            logger.Info(String.Format("{0} Listener ERROR: Classname={1}, MethodName={2}, Error={3}", listener.ListenerType, ClassName, MethodName, err.ToString()));

            if (err.InnerException != null)
                Listener_ListenerException(sender, ClassName, MethodName, err.InnerException);

        }

        public void Listener_ListenerPaused(object sender, string IPAddress, int Port)
        {
            var listener = (XmlServer.Listeners.Listener)sender;
            logger.Info(String.Format("{0} Listener: Paused", listener.ListenerType));
        }

        public void Listener_ListenerRequestCompleted(object sender, string ClientIPAddress, int ClientPort, string ReplyData, int Duration)
        {
            var listener = (XmlServer.Listeners.Listener)sender;
            logger.Info(String.Format("{0} Listener: Request Completed, ReplyData={1}, Duration={2}", listener.ListenerType, ReplyData, Duration));
        }

        public void Listener_ListenerRequestServicing(object sender, string ClientIPAddress, int ClientPort, string RequestData)
        {
            var listener = (XmlServer.Listeners.Listener)sender;
            logger.Info(String.Format("{0} Listener: Request Servicing, RequestData={1}", listener.ListenerType, RequestData));
        }

        public void Listener_ListenerRequestStarted(object sender, string ClientIPAddress, int ClientPort)
        {
            var listener = (XmlServer.Listeners.Listener)sender;
            logger.Info(String.Format("{0} Listener: Request Started", listener.ListenerType));
        }

        public void Listener_ListenerResumed(object sender, string IPAddress, int Port)
        {
            var listener = (XmlServer.Listeners.Listener)sender;
            logger.Info(String.Format("{0} Listener: Resumed", listener.ListenerType));
        }

        public void Listener_ListenerStarted(object sender, string IPAddress, int Port)
        {
            
            var listener = (XmlServer.Listeners.Listener)sender;
            logger.Info(String.Format("{0} Listener: Started", listener.ListenerType));

            System.Console.WriteLine("Operators Loaded:");
            int count = 1;
            foreach (var operatorInvoker in listener.OperatorInvokers)
            {
                logger.Info(String.Format("    {0}: {1}", count, operatorInvoker.Value.FullName));
                count++;
            }
             
        }

        public void Listener_ListenerStopped(object sender, string IPAddress, int Port)
        {
            var listener = (XmlServer.Listeners.Listener)sender;
            logger.Info(String.Format("{0} Listener: Stopped", listener.ListenerType));
        }


        #endregion
    }
}
